This notebook looks at cell type annotations and gene set expression
across all samples in SCPCP00015 and assigns “final”
annotations. To do this we are using the merged, but not
batch-corrected, object containing the gene expression data for all
samples.
The following input is used:
- Annotations obtained by running
SingleR with tumor
cells as a reference output by
aucell-singler-annotation.sh.
- Consensus cell type annotations output from the
cell-type-consensus module.
- AUC values as calculated by
AUCell for a set of Ewing
sarcoma specific gene sets in MSigDB output by
run-aucell-ews-signatures.sh.
Setup
suppressPackageStartupMessages({
# load required packages
library(SingleCellExperiment)
library(ggplot2)
})
# Set default ggplot theme
theme_set(
theme_classic()
)
# set seed
set.seed(2024)
# quiet messages
options(readr.show_col_types = FALSE)
ComplexHeatmap::ht_opt(message = FALSE)
# The base path for the OpenScPCA repository, found by its (hidden) .git directory
repository_base <- rprojroot::find_root(rprojroot::is_git_root)
# The current data directory, found within the repository base directory
data_dir <- file.path(repository_base, "data", "current", "results", "merge-sce", "SCPCP000015")
# The path to this module
module_base <- file.path(repository_base, "analyses", "cell-type-ewings")
# path to sce
sce_file <- file.path(data_dir, "SCPCP000015_merged.rds")
# path to workflow results
workflow_results_dir <- file.path(module_base, "results")
singler_results_dir <- file.path(workflow_results_dir, "aucell_singler_annotation")
singler_results_files <- list.files(singler_results_dir, pattern = "*singler-classifications.tsv", full.names = TRUE, recursive = TRUE)
library_ids <- stringr::str_remove(basename(singler_results_files), "_singler-classifications.tsv")
aucell_results_dir <- file.path(workflow_results_dir, "aucell-ews-signatures")
aucell_results_file <- file.path(aucell_results_dir, "SCPCP000015_auc-ews-gene-signatures.tsv")
consensus_results_dir <- file.path(repository_base, "analyses", "cell-type-consensus", "results", "cell-type-consensus", "SCPCP000015")
consensus_results_files <- list.files(consensus_results_dir, pattern = "*_consensus-cell-type-assignments.tsv.gz", full.names = TRUE, recursive = TRUE)
# small gene sets
visser_marker_genes_file <- file.path(module_base, "references", "visser-all-marker-genes.tsv")
cell_state_genes_file <- file.path(module_base, "references", "tumor-cell-state-markers.tsv")
# marker genes to be used for validating assignments
validation_markers_file <- file.path(module_base, "references", "combined-markers.tsv")
# output file to save final annotations
results_dir <- file.path(module_base, "results", "final-annotations")
output_file <- file.path(results_dir, glue::glue("SCPCP000015_celltype-annotations.tsv.gz"))
# source in setup functions prep_results()
setup_functions <- file.path(module_base, "template_notebooks", "utils", "setup-functions.R")
source(setup_functions)
# source in validation functions
# calculate_mean_markers(), plot_faceted_umap()
validation_functions <- file.path(module_base, "scripts", "utils", "tumor-validation-helpers.R")
source(validation_functions)
# source in plotting functions
# expression_umap(), cluster_density_plot(), and annotated_exp_heatmap()
plotting_functions <- file.path(module_base, "template_notebooks", "utils", "plotting-functions.R")
source(plotting_functions)
stopifnot(
"sce file does not exist" = file.exists(sce_file),
"aucell results file does not exist" = file.exists(aucell_results_file)
)
# read in sce
sce <- readr::read_rds(sce_file)
# read in workflow results
singler_df <- singler_results_files |>
purrr::set_names(library_ids) |>
purrr::map(readr::read_tsv) |>
dplyr::bind_rows(.id = "library_id")
aucell_df <- readr::read_tsv(aucell_results_file) |>
tidyr::separate(barcodes, "-", into = c("library_id", "barcodes"))
# consensus cell types
consensus_df <- consensus_results_files |>
purrr::map(readr::read_tsv) |>
dplyr::bind_rows()
# read in marker genes and combine into one list
visser_markers_df <- readr::read_tsv(visser_marker_genes_file) |>
dplyr::select(cell_type, ensembl_gene_id, gene_symbol) |>
unique()
cell_state_markers_df <- readr::read_tsv(cell_state_genes_file) |>
dplyr::select(cell_type = cell_state, ensembl_gene_id, gene_symbol)
all_markers_df <- dplyr::bind_rows(list(visser_markers_df, cell_state_markers_df))
Prepare data for plotting
all_results_df <- prep_results(
sce,
singler_df = singler_df,
cluster_df = NULL,
aucell_df = aucell_df,
consensus_df = consensus_df,
cluster_nn = params$cluster_nn,
cluster_res = params$cluster_res,
join_columns = c("barcodes", "library_id")
) |>
dplyr::mutate(barcodes = glue::glue("{library_id}-{barcodes}"))
Warning: Using an external vector in selections was deprecated in tidyselect 1.1.0.
Please use `all_of()` or `any_of()` instead.
# Was:
data %>% select(join_columns)
# Now:
data %>% select(all_of(join_columns))
See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
cell_types <- unique(all_markers_df$cell_type)
# get the mean expression of all genes for each cell state
gene_exp_df <- cell_types |>
purrr::map(\(type){
calculate_mean_markers(all_markers_df, sce, type, cell_type)
}) |>
purrr::reduce(dplyr::inner_join, by = "barcodes")
all_info_df <- all_results_df |>
dplyr::left_join(gene_exp_df, by = "barcodes")
Results summary
First, we will just summarize the results from the various inputs and
workflows. Then we will look at those results and assign any tumor cells
and refine annotations as needed.
aucell-singler-annotation assignments
The below UMAP shows the top cell types assigned by
SingleR in the aucell-singler-annotation.sh
workflow. The top 7 cell types are shown and all other cell types are
grouped together as “All remaining cell types”.
plot_faceted_umap(all_info_df, singler_lumped, legend_title = "SingleR cell types") +
theme(strip.text = element_text(size = 8))

Consensus cell type assignments
The below UMAP shows the top cell types for which consensus cell
types were assigned. Consensus cell types are observed when cell types
from SingleR and CellAssign share a common
ancestor. If no consensus is found, the cells are labeled with
“Unknown”. We anticipate that in many cases, the “Unknown” cells will be
tumor cells. The top 7 cell types are shown and all other cell types are
grouped together as “All remaining cell types”.
plot_faceted_umap(all_info_df, consensus_lumped, legend_title = "Consensus cell types") +
theme(strip.text = element_text(size = 8))

AUCell results
The below plots show the AUC values determined by AUCell
and output from run-aucell-ews-signatures.sh. The first
plot shows the individual AUC values on the UMAP.
For context:
- Any gene sets labeled with
up represent
EWS-FLI1 target genes that we expect to be upregulated in
EWS-FLI1 high tumor cells.
- Any gene sets labeled with
down represent
EWS-FLI1 repressed genes that we expect to be downregulated
in EWS-FLI1 high tumor cells and upregulated in
EWS-FLI1 low tumor cells.
- The
aynaud-ews-targets is a list of genes found to be
upregulated in EWS-FLI1 high tumor cells.
- The
wrenn-nt5e-genes is a list of genes found to be
upregulated in EWS-FLI1 low tumor cells/ cancer associated
fibroblasts.
- The
gobp_ECM and hallmark_EMT are gene
sets that are hypothesized to be upregulated in EWS-FLI1
low tumor cells/ cancer associated fibroblasts.
# get the individual thresholds determined by AUCell for each msigdb geneset
# we want this so we can show the threshold on the density plots
auc_threshold_df <- all_info_df |>
tidyr::pivot_longer(starts_with("threshold_auc_"), names_to = "geneset", values_to = "threshold") |>
dplyr::mutate(
geneset = stringr::str_remove(geneset, "threshold_auc_")
) |>
dplyr::select(barcodes, geneset, threshold)
# reformat auc data for density plots and UMAPs showing AUC values
auc_df <- all_info_df |>
tidyr::pivot_longer(starts_with("auc_"), names_to = "geneset", values_to = "auc_value") |>
dplyr::mutate(
geneset = stringr::str_remove(geneset, "auc_")
) |>
dplyr::select(barcodes, UMAP1, UMAP2, geneset, auc_value) |>
dplyr::left_join(auc_threshold_df, by = c("barcodes", "geneset")) |>
dplyr::mutate(
in_geneset = auc_value > threshold
)
expression_umap(auc_df, auc_value, geneset)

The below plot shows the distribution of AUC values for each gene set
colored based on if the AUC value is above the threshold determined by
AUCell, indicated with a dotted line.
ggplot(auc_df, aes(x = auc_value, color = in_geneset, fill = in_geneset)) +
geom_density(alpha = 0.5, bw = 0.01) +
facet_wrap(vars(geneset)) +
ggplot2::geom_vline(data = auc_df,
mapping = aes(xintercept = threshold),
lty = 2) +
theme(
aspect.ratio = 1,
strip.background = element_rect(fill = "transparent", linewidth = 0.5),
panel.border = element_rect(color = "black", fill = NA, linewidth = 0.5)
)

Here we look at the AUC values for each gene set across all consensus
cell types.
auc_columns <- colnames(all_info_df)[which(startsWith(colnames(all_info_df), "auc_"))]
cluster_density_plot(all_info_df, auc_columns, "consensus_lumped", "AUC")

Here we look at the AUC values for each gene set across all
SingleR cell types.
cluster_density_plot(all_info_df, auc_columns, "singler_lumped", "AUC")

The heatmap below shows the AUC values for all cells and all gene
sets and the final refined cell type annotations.
# sort by final cell types
all_info_df <- all_info_df |>
dplyr::arrange(consensus_lumped)
single_annotation_heatmap(
all_info_df,
exp_columns = auc_columns,
cell_type_column = "consensus_lumped",
legend_title = "AUC"
)

Mean expression of custom gene sets
Below we look at the mean expression of all genes in each of the
custom gene sets we have across the consensus cell types. First using a
density plot and then using a heatmap.
mean_exp_columns <- colnames(all_info_df)[which(endsWith(colnames(all_info_df), "_mean"))]
cluster_density_plot(all_info_df, mean_exp_columns, annotation_column = "consensus_lumped", "mean gene expression")

single_annotation_heatmap(
all_info_df,
exp_columns = mean_exp_columns,
cell_type_column = "consensus_lumped",
legend_title = "AUC"
)

Conclusions based on workflow results
Looking at these results, it looks like things labeled as “tumor” by
SingleR and “Unknown” in the consensus cell types have high
AUC values for the EWS-FLI1 upregulated gene sets. We also
see low expression of EWS-FLI1 repressed targets in these
cells. It looks like the cells that are called as muscle cells in both
cell types have high expression of EWS-FLI1 targets. This
makes me think those are actually tumor cells that are
mis-classified.
Intriguingly we see high expression of hallmark_EMT and
the EWS-FLI1 repressed targets in fibroblasts, which also
happen to have high expression of the EWS-FLI1 low targets
identifed by Wrenn et al. (wrenn-nt5e-genes). That
paper identifies EWS-FLI1 low cells as cancer associated
fibroblasts. It’s also important to note that many of the genes that
define the EWS-FLI1 low state are also high in fibroblasts,
so distinguishing them may be difficult. We also see that the
chondrocytes identified in SingleR have high expression of
this gene set.
Another key observation is that we see separation between the AUC
values observed in tumor/ other cells in the density plots, but these
values are much lower than the AUC threshold automatically determined by
AUCell. I think because of that we want to pick gene sets
where we see very clear separation in AUC values between cell types to
aid in finalizing our assignments.
The density plots also show increased expression in the immune
markers in immune cell populations and endothelial cell markers in the
endothelial cells. There may be a small group of cells that have high
expression of the proliferative markers. Perhaps we can label any cells
that are tumor cells and show proliferative markers as
Tumor EWS-high proliferative.
Refining cell type annotations
Here we will combine annotations from SingleR and
consensus cell types with help from the AUCell output.
We’ll use the following criteria:
Tumor EWS-low CAF: High expression of
wrenn-nt5e-genes (> 0.1 AUC) and high expression of
repressed targets using miyagawa_down (> 0.03 AUC)
Tumor EWS-high: High expression of
aynaud-ews-targets (>0.025 AUC) and identified as tumor
with SingleR and identified as either unknown, muscle cell,
or smooth muscle cell in the consensus cell types
Tumor EWS-high proliferative: Cells that meet the
requirements for Tumor EWS-high and have expression of
proliferative markers defined as mean > 0
- All other cell types will be based on the
consensus_annotation
For plotting purposes, we won’t show any of the cell types that only
have a handful of cells. Let’s see what cell types we won’t be looking
at if we only plot the top 9 cell types.
lumped_celltypes <- unique(all_info_df$final_lumped)
all_celltypes <- unique(all_info_df$final_annotation)
missing_celltypes <- setdiff(all_celltypes, lumped_celltypes)
missing_only <- all_info_df |>
dplyr::filter(final_annotation %in% missing_celltypes)
missing_only |>
dplyr::count(final_annotation) |>
dplyr::arrange(desc(final_annotation))
I think we can leave these few cells alone and just lump them as “All
remaining cell types” for plots as we have been doing.
Validation of cell type assignments
In the following section we’ll show some plots to help validate that
we are content with our cell type assignments. These plots will look
specifically at expression of a set of key marker genes that we expect
to up in the assigned cell types. These markers are found in
references/combined-markers.tsv and include a smaller set
of markers for each of the cell types we have identified.
validation_markers_df <- readr::read_tsv(validation_markers_file)
# pull out list of genes
genes <- validation_markers_df |>
dplyr::pull(ensembl_gene_id)
# get individual gene counts for all marker genes
gene_cts <- logcounts(sce[genes, ]) |>
as.matrix() |>
t() |>
as.data.frame()
gene_cts$barcodes <- rownames(gene_cts)
# get all unique cell types from the final lumped group
celltypes_df <- all_info_df |>
dplyr::select(barcodes, final_lumped)
# create a df that has gene expression column and column indicating whether or not the gene is detected in that cell
genes_df <- gene_cts |>
tidyr::pivot_longer(!barcodes, names_to = "ensembl_gene_id", values_to = "gene_exp") |>
dplyr::left_join(celltypes_df, by = c("barcodes")) |>
dplyr::left_join(validation_markers_df, by = c("ensembl_gene_id")) |>
dplyr::rowwise() |>
dplyr::mutate(
detected = gene_exp > 0 # column indicating if gene is present or not
)
# get total number of cells per final annotation group
total_cells_df <- genes_df |>
dplyr::select(barcodes, final_lumped) |>
unique() |>
dplyr::count(final_lumped, name = "total_cells")
# get total number of cells each gene is detected in per group
# and mean gene expression per group
group_stats_df <- genes_df |>
dplyr::group_by(final_lumped, ensembl_gene_id) |>
dplyr::summarize(
detected_count = sum(detected),
mean_exp = mean(gene_exp)
)
`summarise()` has grouped output by 'final_lumped'. You can override using the `.groups` argument.
gene_summary_df <- genes_df |>
# add total cells
dplyr::left_join(total_cells_df, by = c("final_lumped")) |>
# add per gene stats
dplyr::left_join(group_stats_df, by = c("ensembl_gene_id", "final_lumped")) |>
dplyr::select(gene_symbol, final_lumped, cell_type, total_cells, detected_count, mean_exp) |>
unique() |>
dplyr::rowwise() |>
dplyr::mutate(
# get total percent
percent_exp = (detected_count/total_cells) * 100,
# order genes based on cell type they indicate
gene_symbol = factor(gene_symbol, levels = validation_markers_df$gene_symbol),
final_lumped = forcats::fct_relevel(final_lumped, cell_type_order)
)

A few notes from this plot:
- Generally tumor markers are present throughout, although they are
highest in tumor cells.
- Tumor EWS low CAFs show pretty strong expression of the
EWS-low markers. These markers are also present in
fibroblasts, but to a lesser degree. Additionally, TNC
(which has been published to be important in the metastatic phenotype in
Ewing cells) is specific to the low cells and not found in the
fibroblasts, which makes me more confident that those are indeed tumor
cells.
- Endothelial cells show strong expression of
PECAM1 and
VWF, while that is not seen in most of the other
cells.
- All immune cell types show
PTPRC as expected with
macrophages also showing MRC1. The only concern I see is
that the mature T cell does not express either of the
CD3 genes, but the memory T cells do? I’m not totally sure
what to do there since that label is a consensus label so perhaps that
gene is not as well covered in these samples?
Let’s make a heatmap version of the same plot.

And finally we’ll look at our annotations on a UMAP! Because, why
not.

Export cell types
Now that we have our cell types let’s export them to a TSV to save
for future use!
readr::write_tsv(annotation_df, output_file)
Warning: cannot open compressed file '/Users/allyhawkins/Documents/ALSF/forked_repos/OpenScPCA-analysis/analyses/cell-type-ewings/results/final-annotations/SCPCP000015_celltype-annotations.tsv.gz', probable reason 'No such file or directory'Error in open.connection(3L, "wb") : cannot open the connection
Session info
# record the versions of the packages used in this analysis and other environment information
sessionInfo()
LS0tCnRpdGxlOiAiQ2VsbCB0eXBlIGFzc2lnbm1lbnRzIGZvciBTQ1BDUDAwMDAxNSIKYXV0aG9yOiBBbGx5IEhhd2tpbnMKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICBjb2RlX2ZvbGRpbmc6ICJoaWRlIgotLS0KClRoaXMgbm90ZWJvb2sgbG9va3MgYXQgY2VsbCB0eXBlIGFubm90YXRpb25zIGFuZCBnZW5lIHNldCBleHByZXNzaW9uIGFjcm9zcyBhbGwgc2FtcGxlcyBpbiBgU0NQQ1AwMDAxNWAgYW5kIGFzc2lnbnMgImZpbmFsIiBhbm5vdGF0aW9ucy4gClRvIGRvIHRoaXMgd2UgYXJlIHVzaW5nIHRoZSBtZXJnZWQsIGJ1dCBub3QgYmF0Y2gtY29ycmVjdGVkLCBvYmplY3QgY29udGFpbmluZyB0aGUgZ2VuZSBleHByZXNzaW9uIGRhdGEgZm9yIGFsbCBzYW1wbGVzLiAKClRoZSBmb2xsb3dpbmcgaW5wdXQgaXMgdXNlZDogCgotIEFubm90YXRpb25zIG9idGFpbmVkIGJ5IHJ1bm5pbmcgYFNpbmdsZVJgIHdpdGggdHVtb3IgY2VsbHMgYXMgYSByZWZlcmVuY2Ugb3V0cHV0IGJ5IGBhdWNlbGwtc2luZ2xlci1hbm5vdGF0aW9uLnNoYC4gCi0gQ29uc2Vuc3VzIGNlbGwgdHlwZSBhbm5vdGF0aW9ucyBvdXRwdXQgZnJvbSB0aGUgYGNlbGwtdHlwZS1jb25zZW5zdXNgIG1vZHVsZS4gCi0gQVVDIHZhbHVlcyBhcyBjYWxjdWxhdGVkIGJ5IGBBVUNlbGxgIGZvciBhIHNldCBvZiBFd2luZyBzYXJjb21hIHNwZWNpZmljIGdlbmUgc2V0cyBpbiBNU2lnREIgb3V0cHV0IGJ5IGBydW4tYXVjZWxsLWV3cy1zaWduYXR1cmVzLnNoYC4gCgojIyBTZXR1cAoKYGBge3IgcGFja2FnZXN9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgIyBsb2FkIHJlcXVpcmVkIHBhY2thZ2VzCiAgbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKICBsaWJyYXJ5KGdncGxvdDIpCn0pCgojIFNldCBkZWZhdWx0IGdncGxvdCB0aGVtZQp0aGVtZV9zZXQoCiAgdGhlbWVfY2xhc3NpYygpCikKCiMgc2V0IHNlZWQKc2V0LnNlZWQoMjAyNCkKCiMgcXVpZXQgbWVzc2FnZXMKb3B0aW9ucyhyZWFkci5zaG93X2NvbF90eXBlcyA9IEZBTFNFKQpDb21wbGV4SGVhdG1hcDo6aHRfb3B0KG1lc3NhZ2UgPSBGQUxTRSkKYGBgCgoKYGBge3IgYmFzZSBwYXRoc30KIyBUaGUgYmFzZSBwYXRoIGZvciB0aGUgT3BlblNjUENBIHJlcG9zaXRvcnksIGZvdW5kIGJ5IGl0cyAoaGlkZGVuKSAuZ2l0IGRpcmVjdG9yeQpyZXBvc2l0b3J5X2Jhc2UgPC0gcnByb2pyb290OjpmaW5kX3Jvb3QocnByb2pyb290Ojppc19naXRfcm9vdCkKCiMgVGhlIGN1cnJlbnQgZGF0YSBkaXJlY3RvcnksIGZvdW5kIHdpdGhpbiB0aGUgcmVwb3NpdG9yeSBiYXNlIGRpcmVjdG9yeQpkYXRhX2RpciA8LSBmaWxlLnBhdGgocmVwb3NpdG9yeV9iYXNlLCAiZGF0YSIsICJjdXJyZW50IiwgInJlc3VsdHMiLCAibWVyZ2Utc2NlIiwgIlNDUENQMDAwMDE1IikKCiMgVGhlIHBhdGggdG8gdGhpcyBtb2R1bGUKbW9kdWxlX2Jhc2UgPC0gZmlsZS5wYXRoKHJlcG9zaXRvcnlfYmFzZSwgImFuYWx5c2VzIiwgImNlbGwtdHlwZS1ld2luZ3MiKSAKYGBgCgpgYGB7cn0KIyBwYXRoIHRvIHNjZSAKc2NlX2ZpbGUgPC0gZmlsZS5wYXRoKGRhdGFfZGlyLCAiU0NQQ1AwMDAwMTVfbWVyZ2VkLnJkcyIpCgojIHBhdGggdG8gd29ya2Zsb3cgcmVzdWx0cwp3b3JrZmxvd19yZXN1bHRzX2RpciA8LSBmaWxlLnBhdGgobW9kdWxlX2Jhc2UsICJyZXN1bHRzIikKCnNpbmdsZXJfcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKHdvcmtmbG93X3Jlc3VsdHNfZGlyLCAiYXVjZWxsX3NpbmdsZXJfYW5ub3RhdGlvbiIpCnNpbmdsZXJfcmVzdWx0c19maWxlcyA8LSBsaXN0LmZpbGVzKHNpbmdsZXJfcmVzdWx0c19kaXIsIHBhdHRlcm4gPSAiKnNpbmdsZXItY2xhc3NpZmljYXRpb25zLnRzdiIsIGZ1bGwubmFtZXMgPSBUUlVFLCByZWN1cnNpdmUgPSBUUlVFKQpsaWJyYXJ5X2lkcyA8LSBzdHJpbmdyOjpzdHJfcmVtb3ZlKGJhc2VuYW1lKHNpbmdsZXJfcmVzdWx0c19maWxlcyksICJfc2luZ2xlci1jbGFzc2lmaWNhdGlvbnMudHN2IikKCgphdWNlbGxfcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKHdvcmtmbG93X3Jlc3VsdHNfZGlyLCAiYXVjZWxsLWV3cy1zaWduYXR1cmVzIikKYXVjZWxsX3Jlc3VsdHNfZmlsZSA8LSBmaWxlLnBhdGgoYXVjZWxsX3Jlc3VsdHNfZGlyLCAiU0NQQ1AwMDAwMTVfYXVjLWV3cy1nZW5lLXNpZ25hdHVyZXMudHN2IikKCmNvbnNlbnN1c19yZXN1bHRzX2RpciA8LSBmaWxlLnBhdGgocmVwb3NpdG9yeV9iYXNlLCAiYW5hbHlzZXMiLCAiY2VsbC10eXBlLWNvbnNlbnN1cyIsICJyZXN1bHRzIiwgImNlbGwtdHlwZS1jb25zZW5zdXMiLCAiU0NQQ1AwMDAwMTUiKQpjb25zZW5zdXNfcmVzdWx0c19maWxlcyA8LSBsaXN0LmZpbGVzKGNvbnNlbnN1c19yZXN1bHRzX2RpciwgcGF0dGVybiA9ICIqX2NvbnNlbnN1cy1jZWxsLXR5cGUtYXNzaWdubWVudHMudHN2Lmd6IiwgZnVsbC5uYW1lcyA9IFRSVUUsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIHNtYWxsIGdlbmUgc2V0cwp2aXNzZXJfbWFya2VyX2dlbmVzX2ZpbGUgPC0gZmlsZS5wYXRoKG1vZHVsZV9iYXNlLCAicmVmZXJlbmNlcyIsICJ2aXNzZXItYWxsLW1hcmtlci1nZW5lcy50c3YiKQpjZWxsX3N0YXRlX2dlbmVzX2ZpbGUgPC0gZmlsZS5wYXRoKG1vZHVsZV9iYXNlLCAicmVmZXJlbmNlcyIsICJ0dW1vci1jZWxsLXN0YXRlLW1hcmtlcnMudHN2IikKCiMgbWFya2VyIGdlbmVzIHRvIGJlIHVzZWQgZm9yIHZhbGlkYXRpbmcgYXNzaWdubWVudHMgCnZhbGlkYXRpb25fbWFya2Vyc19maWxlIDwtIGZpbGUucGF0aChtb2R1bGVfYmFzZSwgInJlZmVyZW5jZXMiLCAiY29tYmluZWQtdmFsaWRhdGlvbi1tYXJrZXJzLnRzdiIpCmBgYAoKYGBge3J9CiMgb3V0cHV0IGZpbGUgdG8gc2F2ZSBmaW5hbCBhbm5vdGF0aW9ucyAKcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKG1vZHVsZV9iYXNlLCAicmVzdWx0cyIsICJmaW5hbC1hbm5vdGF0aW9ucyIpCmZzOjpkaXJfY3JlYXRlKHJlc3VsdHNfZGlyKQpvdXRwdXRfZmlsZSA8LSBmaWxlLnBhdGgocmVzdWx0c19kaXIsIGdsdWU6OmdsdWUoIlNDUENQMDAwMDE1X2NlbGx0eXBlLWFubm90YXRpb25zLnRzdi5neiIpKQpgYGAKCgpgYGB7cn0KIyBzb3VyY2UgaW4gc2V0dXAgZnVuY3Rpb25zIHByZXBfcmVzdWx0cygpCnNldHVwX2Z1bmN0aW9ucyA8LSBmaWxlLnBhdGgobW9kdWxlX2Jhc2UsICJ0ZW1wbGF0ZV9ub3RlYm9va3MiLCAidXRpbHMiLCAic2V0dXAtZnVuY3Rpb25zLlIiKQpzb3VyY2Uoc2V0dXBfZnVuY3Rpb25zKQoKIyBzb3VyY2UgaW4gdmFsaWRhdGlvbiBmdW5jdGlvbnMgCiMgY2FsY3VsYXRlX21lYW5fbWFya2VycygpLCBwbG90X2ZhY2V0ZWRfdW1hcCgpCnZhbGlkYXRpb25fZnVuY3Rpb25zIDwtIGZpbGUucGF0aChtb2R1bGVfYmFzZSwgInNjcmlwdHMiLCAidXRpbHMiLCAidHVtb3ItdmFsaWRhdGlvbi1oZWxwZXJzLlIiKQpzb3VyY2UodmFsaWRhdGlvbl9mdW5jdGlvbnMpCgojIHNvdXJjZSBpbiBwbG90dGluZyBmdW5jdGlvbnMgCiMgZXhwcmVzc2lvbl91bWFwKCksIGNsdXN0ZXJfZGVuc2l0eV9wbG90KCksIGFuZCBhbm5vdGF0ZWRfZXhwX2hlYXRtYXAoKQpwbG90dGluZ19mdW5jdGlvbnMgPC0gZmlsZS5wYXRoKG1vZHVsZV9iYXNlLCAidGVtcGxhdGVfbm90ZWJvb2tzIiwgInV0aWxzIiwgInBsb3R0aW5nLWZ1bmN0aW9ucy5SIikKc291cmNlKHBsb3R0aW5nX2Z1bmN0aW9ucykKYGBgCgpgYGB7cn0Kc3RvcGlmbm90KAogICJzY2UgZmlsZSBkb2VzIG5vdCBleGlzdCIgPSBmaWxlLmV4aXN0cyhzY2VfZmlsZSksCiAgImF1Y2VsbCByZXN1bHRzIGZpbGUgZG9lcyBub3QgZXhpc3QiID0gZmlsZS5leGlzdHMoYXVjZWxsX3Jlc3VsdHNfZmlsZSkKKQpgYGAKCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KIyByZWFkIGluIHNjZQpzY2UgPC0gcmVhZHI6OnJlYWRfcmRzKHNjZV9maWxlKQoKIyByZWFkIGluIHdvcmtmbG93IHJlc3VsdHMKc2luZ2xlcl9kZiA8LSBzaW5nbGVyX3Jlc3VsdHNfZmlsZXMgfD4gCiAgcHVycnI6OnNldF9uYW1lcyhsaWJyYXJ5X2lkcykgfD4KICBwdXJycjo6bWFwKHJlYWRyOjpyZWFkX3RzdikgfD4gCiAgZHBseXI6OmJpbmRfcm93cyguaWQgPSAibGlicmFyeV9pZCIpCgphdWNlbGxfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KGF1Y2VsbF9yZXN1bHRzX2ZpbGUpIHw+IAogIHRpZHlyOjpzZXBhcmF0ZShiYXJjb2RlcywgIi0iLCBpbnRvID0gYygibGlicmFyeV9pZCIsICJiYXJjb2RlcyIpKQoKIyBjb25zZW5zdXMgY2VsbCB0eXBlcyAKY29uc2Vuc3VzX2RmIDwtIGNvbnNlbnN1c19yZXN1bHRzX2ZpbGVzIHw+IAogIHB1cnJyOjptYXAocmVhZHI6OnJlYWRfdHN2KSB8PiAKICBkcGx5cjo6YmluZF9yb3dzKCkKCiMgcmVhZCBpbiBtYXJrZXIgZ2VuZXMgYW5kIGNvbWJpbmUgaW50byBvbmUgbGlzdCAKdmlzc2VyX21hcmtlcnNfZGYgPC0gcmVhZHI6OnJlYWRfdHN2KHZpc3Nlcl9tYXJrZXJfZ2VuZXNfZmlsZSkgfD4gCiAgZHBseXI6OnNlbGVjdChjZWxsX3R5cGUsIGVuc2VtYmxfZ2VuZV9pZCwgZ2VuZV9zeW1ib2wpIHw+IAogIHVuaXF1ZSgpCiAgCmNlbGxfc3RhdGVfbWFya2Vyc19kZiA8LSByZWFkcjo6cmVhZF90c3YoY2VsbF9zdGF0ZV9nZW5lc19maWxlKSB8PiAKICBkcGx5cjo6c2VsZWN0KGNlbGxfdHlwZSA9IGNlbGxfc3RhdGUsIGVuc2VtYmxfZ2VuZV9pZCwgZ2VuZV9zeW1ib2wpCgphbGxfbWFya2Vyc19kZiA8LSBkcGx5cjo6YmluZF9yb3dzKGxpc3Qodmlzc2VyX21hcmtlcnNfZGYsIGNlbGxfc3RhdGVfbWFya2Vyc19kZikpCmBgYAoKIyMgUHJlcGFyZSBkYXRhIGZvciBwbG90dGluZwoKYGBge3J9CmFsbF9yZXN1bHRzX2RmIDwtIHByZXBfcmVzdWx0cygKICBzY2UsIAogIHNpbmdsZXJfZGYgPSBzaW5nbGVyX2RmLCAKICBjbHVzdGVyX2RmID0gTlVMTCwgCiAgYXVjZWxsX2RmID0gYXVjZWxsX2RmLAogIGNvbnNlbnN1c19kZiA9IGNvbnNlbnN1c19kZiwKICBjbHVzdGVyX25uID0gcGFyYW1zJGNsdXN0ZXJfbm4sCiAgY2x1c3Rlcl9yZXMgPSBwYXJhbXMkY2x1c3Rlcl9yZXMsCiAgam9pbl9jb2x1bW5zID0gYygiYmFyY29kZXMiLCAibGlicmFyeV9pZCIpCiAgKSB8PgogIGRwbHlyOjptdXRhdGUoYmFyY29kZXMgPSBnbHVlOjpnbHVlKCJ7bGlicmFyeV9pZH0te2JhcmNvZGVzfSIpKQogIApjZWxsX3R5cGVzIDwtIHVuaXF1ZShhbGxfbWFya2Vyc19kZiRjZWxsX3R5cGUpCgojIGdldCB0aGUgbWVhbiBleHByZXNzaW9uIG9mIGFsbCBnZW5lcyBmb3IgZWFjaCBjZWxsIHN0YXRlCmdlbmVfZXhwX2RmIDwtIGNlbGxfdHlwZXMgfD4KICBwdXJycjo6bWFwKFwodHlwZSl7CiAgICBjYWxjdWxhdGVfbWVhbl9tYXJrZXJzKGFsbF9tYXJrZXJzX2RmLCBzY2UsIHR5cGUsIGNlbGxfdHlwZSkKICB9KSB8PgogIHB1cnJyOjpyZWR1Y2UoZHBseXI6OmlubmVyX2pvaW4sIGJ5ID0gImJhcmNvZGVzIikKCmFsbF9pbmZvX2RmIDwtIGFsbF9yZXN1bHRzX2RmIHw+IAogIGRwbHlyOjpsZWZ0X2pvaW4oZ2VuZV9leHBfZGYsIGJ5ID0gImJhcmNvZGVzIikKYGBgCgojIyBSZXN1bHRzIHN1bW1hcnkKCkZpcnN0LCB3ZSB3aWxsIGp1c3Qgc3VtbWFyaXplIHRoZSByZXN1bHRzIGZyb20gdGhlIHZhcmlvdXMgaW5wdXRzIGFuZCB3b3JrZmxvd3MuIApUaGVuIHdlIHdpbGwgbG9vayBhdCB0aG9zZSByZXN1bHRzIGFuZCBhc3NpZ24gYW55IHR1bW9yIGNlbGxzIGFuZCByZWZpbmUgYW5ub3RhdGlvbnMgYXMgbmVlZGVkLiAKCiMjIyBgYXVjZWxsLXNpbmdsZXItYW5ub3RhdGlvbmAgYXNzaWdubWVudHMgCgpUaGUgYmVsb3cgVU1BUCBzaG93cyB0aGUgdG9wIGNlbGwgdHlwZXMgYXNzaWduZWQgYnkgYFNpbmdsZVJgIGluIHRoZSBgYXVjZWxsLXNpbmdsZXItYW5ub3RhdGlvbi5zaGAgd29ya2Zsb3cuIApUaGUgdG9wIDcgY2VsbCB0eXBlcyBhcmUgc2hvd24gYW5kIGFsbCBvdGhlciBjZWxsIHR5cGVzIGFyZSBncm91cGVkIHRvZ2V0aGVyIGFzICJBbGwgcmVtYWluaW5nIGNlbGwgdHlwZXMiLiAKCmBgYHtyLCBmaWcuaGVpZ2h0ID0gNX0KcGxvdF9mYWNldGVkX3VtYXAoYWxsX2luZm9fZGYsIHNpbmdsZXJfbHVtcGVkLCBsZWdlbmRfdGl0bGUgPSAiU2luZ2xlUiBjZWxsIHR5cGVzIikgKwogIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQpgYGAKCgojIyMgQ29uc2Vuc3VzIGNlbGwgdHlwZSBhc3NpZ25tZW50cyAKClRoZSBiZWxvdyBVTUFQIHNob3dzIHRoZSB0b3AgY2VsbCB0eXBlcyBmb3Igd2hpY2ggY29uc2Vuc3VzIGNlbGwgdHlwZXMgd2VyZSBhc3NpZ25lZC4KQ29uc2Vuc3VzIGNlbGwgdHlwZXMgYXJlIG9ic2VydmVkIHdoZW4gY2VsbCB0eXBlcyBmcm9tIGBTaW5nbGVSYCBhbmQgYENlbGxBc3NpZ25gIHNoYXJlIGEgY29tbW9uIGFuY2VzdG9yLiAKSWYgbm8gY29uc2Vuc3VzIGlzIGZvdW5kLCB0aGUgY2VsbHMgYXJlIGxhYmVsZWQgd2l0aCAiVW5rbm93biIuIApXZSBhbnRpY2lwYXRlIHRoYXQgaW4gbWFueSBjYXNlcywgdGhlICJVbmtub3duIiBjZWxscyB3aWxsIGJlIHR1bW9yIGNlbGxzLgpUaGUgdG9wIDcgY2VsbCB0eXBlcyBhcmUgc2hvd24gYW5kIGFsbCBvdGhlciBjZWxsIHR5cGVzIGFyZSBncm91cGVkIHRvZ2V0aGVyIGFzICJBbGwgcmVtYWluaW5nIGNlbGwgdHlwZXMiLiAKCmBgYHtyLCBmaWcuaGVpZ2h0PTV9CnBsb3RfZmFjZXRlZF91bWFwKGFsbF9pbmZvX2RmLCBjb25zZW5zdXNfbHVtcGVkLCBsZWdlbmRfdGl0bGUgPSAiQ29uc2Vuc3VzIGNlbGwgdHlwZXMiKSArCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCkpCmBgYAoKCiMjIyBgQVVDZWxsYCByZXN1bHRzIAoKVGhlIGJlbG93IHBsb3RzIHNob3cgdGhlIEFVQyB2YWx1ZXMgZGV0ZXJtaW5lZCBieSBgQVVDZWxsYCBhbmQgb3V0cHV0IGZyb20gYHJ1bi1hdWNlbGwtZXdzLXNpZ25hdHVyZXMuc2hgLiAKVGhlIGZpcnN0IHBsb3Qgc2hvd3MgdGhlIGluZGl2aWR1YWwgQVVDIHZhbHVlcyBvbiB0aGUgVU1BUC4gCgpGb3IgY29udGV4dDogIAoKLSBBbnkgZ2VuZSBzZXRzIGxhYmVsZWQgd2l0aCBgdXBgIHJlcHJlc2VudCBgRVdTLUZMSTFgIHRhcmdldCBnZW5lcyB0aGF0IHdlIGV4cGVjdCB0byBiZSB1cHJlZ3VsYXRlZCBpbiBgRVdTLUZMSTFgIGhpZ2ggdHVtb3IgY2VsbHMuIAotIEFueSBnZW5lIHNldHMgbGFiZWxlZCB3aXRoIGBkb3duYCByZXByZXNlbnQgYEVXUy1GTEkxYCByZXByZXNzZWQgZ2VuZXMgdGhhdCB3ZSBleHBlY3QgdG8gYmUgZG93bnJlZ3VsYXRlZCBpbiBgRVdTLUZMSTFgIGhpZ2ggdHVtb3IgY2VsbHMgYW5kIHVwcmVndWxhdGVkIGluIGBFV1MtRkxJMWAgbG93IHR1bW9yIGNlbGxzLiAKLSBUaGUgYGF5bmF1ZC1ld3MtdGFyZ2V0c2AgaXMgYSBsaXN0IG9mIGdlbmVzIGZvdW5kIHRvIGJlIHVwcmVndWxhdGVkIGluIGBFV1MtRkxJMWAgaGlnaCB0dW1vciBjZWxscy4gCi0gVGhlIGB3cmVubi1udDVlLWdlbmVzYCBpcyBhIGxpc3Qgb2YgZ2VuZXMgZm91bmQgdG8gYmUgdXByZWd1bGF0ZWQgaW4gYEVXUy1GTEkxYCBsb3cgdHVtb3IgY2VsbHMvIGNhbmNlciBhc3NvY2lhdGVkIGZpYnJvYmxhc3RzLgotIFRoZSBgZ29icF9FQ01gIGFuZCBgaGFsbG1hcmtfRU1UYCBhcmUgZ2VuZSBzZXRzIHRoYXQgYXJlIGh5cG90aGVzaXplZCB0byBiZSB1cHJlZ3VsYXRlZCBpbiBgRVdTLUZMSTFgIGxvdyB0dW1vciBjZWxscy8gY2FuY2VyIGFzc29jaWF0ZWQgZmlicm9ibGFzdHMuCgpgYGB7cn0KIyBnZXQgdGhlIGluZGl2aWR1YWwgdGhyZXNob2xkcyBkZXRlcm1pbmVkIGJ5IEFVQ2VsbCBmb3IgZWFjaCBtc2lnZGIgZ2VuZXNldAojIHdlIHdhbnQgdGhpcyBzbyB3ZSBjYW4gc2hvdyB0aGUgdGhyZXNob2xkIG9uIHRoZSBkZW5zaXR5IHBsb3RzIAphdWNfdGhyZXNob2xkX2RmIDwtIGFsbF9pbmZvX2RmIHw+IAogIHRpZHlyOjpwaXZvdF9sb25nZXIoc3RhcnRzX3dpdGgoInRocmVzaG9sZF9hdWNfIiksIG5hbWVzX3RvID0gImdlbmVzZXQiLCB2YWx1ZXNfdG8gPSAidGhyZXNob2xkIikgfD4gCiAgZHBseXI6Om11dGF0ZSgKICAgIGdlbmVzZXQgPSBzdHJpbmdyOjpzdHJfcmVtb3ZlKGdlbmVzZXQsICJ0aHJlc2hvbGRfYXVjXyIpCiAgKSB8PiAKICBkcGx5cjo6c2VsZWN0KGJhcmNvZGVzLCBnZW5lc2V0LCB0aHJlc2hvbGQpCgojIHJlZm9ybWF0IGF1YyBkYXRhIGZvciBkZW5zaXR5IHBsb3RzIGFuZCBVTUFQcyBzaG93aW5nIEFVQyB2YWx1ZXMgCmF1Y19kZiA8LSBhbGxfaW5mb19kZiB8PiAKICB0aWR5cjo6cGl2b3RfbG9uZ2VyKHN0YXJ0c193aXRoKCJhdWNfIiksIG5hbWVzX3RvID0gImdlbmVzZXQiLCB2YWx1ZXNfdG8gPSAiYXVjX3ZhbHVlIikgfD4gCiAgZHBseXI6Om11dGF0ZSgKICAgIGdlbmVzZXQgPSBzdHJpbmdyOjpzdHJfcmVtb3ZlKGdlbmVzZXQsICJhdWNfIikKICApIHw+IAogIGRwbHlyOjpzZWxlY3QoYmFyY29kZXMsIFVNQVAxLCBVTUFQMiwgZ2VuZXNldCwgYXVjX3ZhbHVlKSB8PiAKICBkcGx5cjo6bGVmdF9qb2luKGF1Y190aHJlc2hvbGRfZGYsIGJ5ID0gYygiYmFyY29kZXMiLCAiZ2VuZXNldCIpKSB8PiAKICBkcGx5cjo6bXV0YXRlKAogICAgaW5fZ2VuZXNldCA9ICBhdWNfdmFsdWUgPiB0aHJlc2hvbGQKICApCmBgYAoKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CmV4cHJlc3Npb25fdW1hcChhdWNfZGYsIGF1Y192YWx1ZSwgZ2VuZXNldCkKYGBgCgpUaGUgYmVsb3cgcGxvdCBzaG93cyB0aGUgZGlzdHJpYnV0aW9uIG9mIEFVQyB2YWx1ZXMgZm9yIGVhY2ggZ2VuZSBzZXQgY29sb3JlZCBiYXNlZCBvbiBpZiB0aGUgQVVDIHZhbHVlIGlzIGFib3ZlIHRoZSB0aHJlc2hvbGQgZGV0ZXJtaW5lZCBieSBgQVVDZWxsYCwgaW5kaWNhdGVkIHdpdGggYSBkb3R0ZWQgbGluZS4gCgoKYGBge3IsIGZpZy5oZWlnaHQ9NX0KCmdncGxvdChhdWNfZGYsIGFlcyh4ID0gYXVjX3ZhbHVlLCBjb2xvciA9IGluX2dlbmVzZXQsIGZpbGwgPSBpbl9nZW5lc2V0KSkgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNSwgYncgPSAwLjAxKSArCiAgZmFjZXRfd3JhcCh2YXJzKGdlbmVzZXQpKSArCiAgZ2dwbG90Mjo6Z2VvbV92bGluZShkYXRhID0gYXVjX2RmLAogICAgICAgICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4aW50ZXJjZXB0ID0gdGhyZXNob2xkKSwKICAgICAgICAgICAgICAgICAgICAgIGx0eSA9IDIpICsKICB0aGVtZSgKICAgICAgYXNwZWN0LnJhdGlvID0gMSwKICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiwgbGluZXdpZHRoID0gMC41KSwKICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG9yID0gImJsYWNrIiwgZmlsbCA9IE5BLCBsaW5ld2lkdGggPSAwLjUpCiAgICApIApgYGAKCgpIZXJlIHdlIGxvb2sgYXQgdGhlIEFVQyB2YWx1ZXMgZm9yIGVhY2ggZ2VuZSBzZXQgYWNyb3NzIGFsbCBjb25zZW5zdXMgY2VsbCB0eXBlcy4gIAoKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQphdWNfY29sdW1ucyA8LSBjb2xuYW1lcyhhbGxfaW5mb19kZilbd2hpY2goc3RhcnRzV2l0aChjb2xuYW1lcyhhbGxfaW5mb19kZiksICJhdWNfIikpXQpjbHVzdGVyX2RlbnNpdHlfcGxvdChhbGxfaW5mb19kZiwgYXVjX2NvbHVtbnMsICJjb25zZW5zdXNfbHVtcGVkIiwgIkFVQyIpCmBgYAoKSGVyZSB3ZSBsb29rIGF0IHRoZSBBVUMgdmFsdWVzIGZvciBlYWNoIGdlbmUgc2V0IGFjcm9zcyBhbGwgYFNpbmdsZVJgIGNlbGwgdHlwZXMuICAKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpjbHVzdGVyX2RlbnNpdHlfcGxvdChhbGxfaW5mb19kZiwgYXVjX2NvbHVtbnMsICJzaW5nbGVyX2x1bXBlZCIsICJBVUMiKQpgYGAKClRoZSBoZWF0bWFwIGJlbG93IHNob3dzIHRoZSBBVUMgdmFsdWVzIGZvciBhbGwgY2VsbHMgYW5kIGFsbCBnZW5lIHNldHMgYW5kIHRoZSBmaW5hbCByZWZpbmVkIGNlbGwgdHlwZSBhbm5vdGF0aW9ucy4gCgpgYGB7cn0KIyBzb3J0IGJ5IGZpbmFsIGNlbGwgdHlwZXMgCmFsbF9pbmZvX2RmIDwtIGFsbF9pbmZvX2RmIHw+IAogIGRwbHlyOjphcnJhbmdlKGNvbnNlbnN1c19sdW1wZWQpCgpzaW5nbGVfYW5ub3RhdGlvbl9oZWF0bWFwKAogIGFsbF9pbmZvX2RmLCAKICBleHBfY29sdW1ucyA9IGF1Y19jb2x1bW5zLCAKICBjZWxsX3R5cGVfY29sdW1uID0gImNvbnNlbnN1c19sdW1wZWQiLCAKICBsZWdlbmRfdGl0bGUgPSAiQVVDIgopCmBgYAoKCgojIyMgTWVhbiBleHByZXNzaW9uIG9mIGN1c3RvbSBnZW5lIHNldHMKCkJlbG93IHdlIGxvb2sgYXQgdGhlIG1lYW4gZXhwcmVzc2lvbiBvZiBhbGwgZ2VuZXMgaW4gZWFjaCBvZiB0aGUgY3VzdG9tIGdlbmUgc2V0cyB3ZSBoYXZlIGFjcm9zcyB0aGUgY29uc2Vuc3VzIGNlbGwgdHlwZXMuIApGaXJzdCB1c2luZyBhIGRlbnNpdHkgcGxvdCBhbmQgdGhlbiB1c2luZyBhIGhlYXRtYXAuIAoKYGBge3IsIGZpZy5oZWlnaHQ9Nywgd2FybmluZz1GQUxTRX0KbWVhbl9leHBfY29sdW1ucyA8LSBjb2xuYW1lcyhhbGxfaW5mb19kZilbd2hpY2goZW5kc1dpdGgoY29sbmFtZXMoYWxsX2luZm9fZGYpLCAiX21lYW4iKSldCmNsdXN0ZXJfZGVuc2l0eV9wbG90KGFsbF9pbmZvX2RmLCBtZWFuX2V4cF9jb2x1bW5zLCBhbm5vdGF0aW9uX2NvbHVtbiA9ICJjb25zZW5zdXNfbHVtcGVkIiwgIm1lYW4gZ2VuZSBleHByZXNzaW9uIikKYGBgCmBgYHtyfQpzaW5nbGVfYW5ub3RhdGlvbl9oZWF0bWFwKAogIGFsbF9pbmZvX2RmLCAKICBleHBfY29sdW1ucyA9IG1lYW5fZXhwX2NvbHVtbnMsIAogIGNlbGxfdHlwZV9jb2x1bW4gPSAiY29uc2Vuc3VzX2x1bXBlZCIsIAogIGxlZ2VuZF90aXRsZSA9ICJBVUMiCikKYGBgCgojIyMgQ29uY2x1c2lvbnMgYmFzZWQgb24gd29ya2Zsb3cgcmVzdWx0cyAKCkxvb2tpbmcgYXQgdGhlc2UgcmVzdWx0cywgaXQgbG9va3MgbGlrZSB0aGluZ3MgbGFiZWxlZCBhcyAidHVtb3IiIGJ5IGBTaW5nbGVSYCBhbmQgIlVua25vd24iIGluIHRoZSBjb25zZW5zdXMgY2VsbCB0eXBlcyBoYXZlIGhpZ2ggQVVDIHZhbHVlcyBmb3IgdGhlIGBFV1MtRkxJMWAgdXByZWd1bGF0ZWQgZ2VuZSBzZXRzLiAKV2UgYWxzbyBzZWUgbG93IGV4cHJlc3Npb24gb2YgYEVXUy1GTEkxYCByZXByZXNzZWQgdGFyZ2V0cyBpbiB0aGVzZSBjZWxscy4gCkl0IGxvb2tzIGxpa2UgdGhlIGNlbGxzIHRoYXQgYXJlIGNhbGxlZCBhcyBtdXNjbGUgY2VsbHMgaW4gYm90aCBjZWxsIHR5cGVzIGhhdmUgaGlnaCBleHByZXNzaW9uIG9mIGBFV1MtRkxJMWAgdGFyZ2V0cy4gClRoaXMgbWFrZXMgbWUgdGhpbmsgdGhvc2UgYXJlIGFjdHVhbGx5IHR1bW9yIGNlbGxzIHRoYXQgYXJlIG1pcy1jbGFzc2lmaWVkLgoKSW50cmlndWluZ2x5IHdlIHNlZSBoaWdoIGV4cHJlc3Npb24gb2YgYGhhbGxtYXJrX0VNVGAgYW5kIHRoZSBgRVdTLUZMSTFgIHJlcHJlc3NlZCB0YXJnZXRzIGluIGZpYnJvYmxhc3RzLCB3aGljaCBhbHNvIGhhcHBlbiB0byBoYXZlIGhpZ2ggZXhwcmVzc2lvbiBvZiB0aGUgYEVXUy1GTEkxYCBsb3cgdGFyZ2V0cyBpZGVudGlmZWQgYnkgV3Jlbm4gX2V0IGFsLl8gKGB3cmVubi1udDVlLWdlbmVzYCkuIApUaGF0IHBhcGVyIGlkZW50aWZpZXMgYEVXUy1GTEkxYCBsb3cgY2VsbHMgYXMgY2FuY2VyIGFzc29jaWF0ZWQgZmlicm9ibGFzdHMuCkl0J3MgYWxzbyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IG1hbnkgb2YgdGhlIGdlbmVzIHRoYXQgZGVmaW5lIHRoZSBgRVdTLUZMSTFgIGxvdyBzdGF0ZSBhcmUgYWxzbyBoaWdoIGluIGZpYnJvYmxhc3RzLCBzbyBkaXN0aW5ndWlzaGluZyB0aGVtIG1heSBiZSBkaWZmaWN1bHQuIApXZSBhbHNvIHNlZSB0aGF0IHRoZSBjaG9uZHJvY3l0ZXMgaWRlbnRpZmllZCBpbiBgU2luZ2xlUmAgaGF2ZSBoaWdoIGV4cHJlc3Npb24gb2YgdGhpcyBnZW5lIHNldC4gCgpBbm90aGVyIGtleSBvYnNlcnZhdGlvbiBpcyB0aGF0IHdlIHNlZSBzZXBhcmF0aW9uIGJldHdlZW4gdGhlIEFVQyB2YWx1ZXMgb2JzZXJ2ZWQgaW4gdHVtb3IvIG90aGVyIGNlbGxzIGluIHRoZSBkZW5zaXR5IHBsb3RzLCBidXQgdGhlc2UgdmFsdWVzIGFyZSBtdWNoIGxvd2VyIHRoYW4gdGhlIEFVQyB0aHJlc2hvbGQgYXV0b21hdGljYWxseSBkZXRlcm1pbmVkIGJ5IGBBVUNlbGxgLiAKSSB0aGluayBiZWNhdXNlIG9mIHRoYXQgd2Ugd2FudCB0byBwaWNrIGdlbmUgc2V0cyB3aGVyZSB3ZSBzZWUgdmVyeSBjbGVhciBzZXBhcmF0aW9uIGluIEFVQyB2YWx1ZXMgYmV0d2VlbiBjZWxsIHR5cGVzIHRvIGFpZCBpbiBmaW5hbGl6aW5nIG91ciBhc3NpZ25tZW50cy4gCgpUaGUgZGVuc2l0eSBwbG90cyBhbHNvIHNob3cgaW5jcmVhc2VkIGV4cHJlc3Npb24gaW4gdGhlIGltbXVuZSBtYXJrZXJzIGluIGltbXVuZSBjZWxsIHBvcHVsYXRpb25zIGFuZCBlbmRvdGhlbGlhbCBjZWxsIG1hcmtlcnMgaW4gdGhlIGVuZG90aGVsaWFsIGNlbGxzLiAKVGhlcmUgbWF5IGJlIGEgc21hbGwgZ3JvdXAgb2YgY2VsbHMgdGhhdCBoYXZlIGhpZ2ggZXhwcmVzc2lvbiBvZiB0aGUgcHJvbGlmZXJhdGl2ZSBtYXJrZXJzLiAKUGVyaGFwcyB3ZSBjYW4gbGFiZWwgYW55IGNlbGxzIHRoYXQgYXJlIHR1bW9yIGNlbGxzIGFuZCBzaG93IHByb2xpZmVyYXRpdmUgbWFya2VycyBhcyBgVHVtb3IgRVdTLWhpZ2ggcHJvbGlmZXJhdGl2ZWAuIAoKIyMgUmVmaW5pbmcgY2VsbCB0eXBlIGFubm90YXRpb25zCgpIZXJlIHdlIHdpbGwgY29tYmluZSBhbm5vdGF0aW9ucyBmcm9tIGBTaW5nbGVSYCBhbmQgY29uc2Vuc3VzIGNlbGwgdHlwZXMgd2l0aCBoZWxwIGZyb20gdGhlIGBBVUNlbGxgIG91dHB1dC4gCldlJ2xsIHVzZSB0aGUgZm9sbG93aW5nIGNyaXRlcmlhOiAKCi0gYFR1bW9yIEVXUy1sb3cgQ0FGYDogSGlnaCBleHByZXNzaW9uIG9mIGB3cmVubi1udDVlLWdlbmVzYCAoPiAwLjEgQVVDKSBhbmQgaGlnaCBleHByZXNzaW9uIG9mIHJlcHJlc3NlZCB0YXJnZXRzIHVzaW5nIGBtaXlhZ2F3YV9kb3duYCAoPiAwLjAzIEFVQykKLSBgVHVtb3IgRVdTLWhpZ2hgOiBIaWdoIGV4cHJlc3Npb24gb2YgYGF5bmF1ZC1ld3MtdGFyZ2V0c2AgKD4wLjAyNSBBVUMpIGFuZCBpZGVudGlmaWVkIGFzIHR1bW9yIHdpdGggYFNpbmdsZVJgIGFuZCBpZGVudGlmaWVkIGFzIGVpdGhlciB1bmtub3duLCBtdXNjbGUgY2VsbCwgb3Igc21vb3RoIG11c2NsZSBjZWxsIGluIHRoZSBjb25zZW5zdXMgY2VsbCB0eXBlcwotIGBUdW1vciBFV1MtaGlnaCBwcm9saWZlcmF0aXZlYDogQ2VsbHMgdGhhdCBtZWV0IHRoZSByZXF1aXJlbWVudHMgZm9yIGBUdW1vciBFV1MtaGlnaGAgYW5kIGhhdmUgZXhwcmVzc2lvbiBvZiBwcm9saWZlcmF0aXZlIG1hcmtlcnMgZGVmaW5lZCBhcyBtZWFuID4gMAotIEFsbCBvdGhlciBjZWxsIHR5cGVzIHdpbGwgYmUgYmFzZWQgb24gdGhlIGBjb25zZW5zdXNfYW5ub3RhdGlvbmAKCmBgYHtyfQojIHNldCBkZXNpcmVkIG9yZGVyIGZvciBwbG90dGluZyBmaW5hbCBhbm5vdGF0aW9ucyAKY2VsbF90eXBlX29yZGVyIDwtIGMoCiAgInR1bW9yIEVXUy1oaWdoIiwKICAidHVtb3IgRVdTLWhpZ2ggcHJvbGlmZXJhdGl2ZSIsCiAgInR1bW9yIEVXUy1sb3cgQ0FGIiwKICAiZmlicm9ibGFzdCIsCiAgImVuZG90aGVsaWFsIGNlbGwiLAogICJtYWNyb3BoYWdlIiwKICAibWF0dXJlIFQgY2VsbCIsCiAgIm1lbW9yeSBUIGNlbGwiLAogICJVbmtub3duIiwKICAiQWxsIHJlbWFpbmluZyBjZWxsIHR5cGVzIgopCgojIGRlZmluZSAiZmluYWwuZmluYWwiIGNlbGwgdHlwZXMgCmFsbF9pbmZvX2RmIDwtIGFsbF9pbmZvX2RmIHw+IAogIGRwbHlyOjptdXRhdGUoCiAgICBmaW5hbF9hbm5vdGF0aW9uID0gZHBseXI6OmNhc2Vfd2hlbigKICAgICAgYGF1Y193cmVubi1udDVlLWdlbmVzYCA+IDAuMSAmIGF1Y19taXlhZ2F3YV9kb3duID4gMC4wMyB+ICJ0dW1vciBFV1MtbG93IENBRiIsCiAgICAgIGBhdWNfYXluYXVkLWV3cy10YXJnZXRzYCA+IDAuMDI1IHwgCiAgICAgICAgY29uc2Vuc3VzX2Fubm90YXRpb24gJWluJSBjKCJVbmtub3duIiwgIm11c2NsZSBjZWxsIiwgInNtb290aCBtdXNjbGUgY2VsbCIpICYKICAgICAgICBzaW5nbGVyX2x1bXBlZCA9PSAidHVtb3IiIH4gInR1bW9yIEVXUy1oaWdoIiwKICAgICAgLmRlZmF1bHQgPSBjb25zZW5zdXNfYW5ub3RhdGlvbgogICAgKSwKICAgIGZpbmFsX2Fubm90YXRpb24gPSBkcGx5cjo6aWZfZWxzZSgKICAgICAgZmluYWxfYW5ub3RhdGlvbiA9PSAidHVtb3IgRVdTLWhpZ2giICYgcHJvbGlmZXJhdGl2ZV9tZWFuID4gMCwKICAgICAgInR1bW9yIEVXUy1oaWdoIHByb2xpZmVyYXRpdmUiLAogICAgICBmaW5hbF9hbm5vdGF0aW9uCiAgICApLAogICAgIyBsdW1wIHRvZ2V0aGVyIGZvciBlYXNpZXIgcGxvdHRpbmcKICAgIGZpbmFsX2x1bXBlZCA9IGZpbmFsX2Fubm90YXRpb24gfD4KICAgICAgZm9yY2F0czo6ZmN0X2x1bXBfbig5LCBvdGhlcl9sZXZlbCA9ICJBbGwgcmVtYWluaW5nIGNlbGwgdHlwZXMiLCB0aWVzLm1ldGhvZCA9ICJmaXJzdCIpIHw+CiAgICAgIGZvcmNhdHM6OmZjdF9pbmZyZXEoKSB8PgogICAgICBmb3JjYXRzOjpmY3RfcmVsZXZlbChjZWxsX3R5cGVfb3JkZXIpCiAgKQoKYWxsX2luZm9fZGYgfD4gCiAgZHBseXI6OmNvdW50KGZpbmFsX2Fubm90YXRpb24pIHw+IAogIGRwbHlyOjphcnJhbmdlKGRlc2MobikpCmBgYApGb3IgcGxvdHRpbmcgcHVycG9zZXMsIHdlIHdvbid0IHNob3cgYW55IG9mIHRoZSBjZWxsIHR5cGVzIHRoYXQgb25seSBoYXZlIGEgaGFuZGZ1bCBvZiBjZWxscy4gCkxldCdzIHNlZSB3aGF0IGNlbGwgdHlwZXMgd2Ugd29uJ3QgYmUgbG9va2luZyBhdCBpZiB3ZSBvbmx5IHBsb3QgdGhlIHRvcCA5IGNlbGwgdHlwZXMuIAoKYGBge3J9Cmx1bXBlZF9jZWxsdHlwZXMgPC0gdW5pcXVlKGFsbF9pbmZvX2RmJGZpbmFsX2x1bXBlZCkKYWxsX2NlbGx0eXBlcyA8LSB1bmlxdWUoYWxsX2luZm9fZGYkZmluYWxfYW5ub3RhdGlvbikKbWlzc2luZ19jZWxsdHlwZXMgPC0gc2V0ZGlmZihhbGxfY2VsbHR5cGVzLCBsdW1wZWRfY2VsbHR5cGVzKQoKbWlzc2luZ19vbmx5IDwtIGFsbF9pbmZvX2RmIHw+IAogIGRwbHlyOjpmaWx0ZXIoZmluYWxfYW5ub3RhdGlvbiAlaW4lIG1pc3NpbmdfY2VsbHR5cGVzKQoKbWlzc2luZ19vbmx5IHw+IAogIGRwbHlyOjpjb3VudChmaW5hbF9hbm5vdGF0aW9uKSB8PiAKICBkcGx5cjo6YXJyYW5nZShkZXNjKG4pKQpgYGAKCkkgdGhpbmsgd2UgY2FuIGxlYXZlIHRoZXNlIGZldyBjZWxscyBhbG9uZSBhbmQganVzdCBsdW1wIHRoZW0gYXMgIkFsbCByZW1haW5pbmcgY2VsbCB0eXBlcyIgZm9yIHBsb3RzIGFzIHdlIGhhdmUgYmVlbiBkb2luZy4gCgojIyBWYWxpZGF0aW9uIG9mIGNlbGwgdHlwZSBhc3NpZ25tZW50cwoKSW4gdGhlIGZvbGxvd2luZyBzZWN0aW9uIHdlJ2xsIHNob3cgc29tZSBwbG90cyB0byBoZWxwIHZhbGlkYXRlIHRoYXQgd2UgYXJlIGNvbnRlbnQgd2l0aCBvdXIgY2VsbCB0eXBlIGFzc2lnbm1lbnRzLiAKVGhlc2UgcGxvdHMgd2lsbCBsb29rIHNwZWNpZmljYWxseSBhdCBleHByZXNzaW9uIG9mIGEgc2V0IG9mIGtleSBtYXJrZXIgZ2VuZXMgdGhhdCB3ZSBleHBlY3QgdG8gdXAgaW4gdGhlIGFzc2lnbmVkIGNlbGwgdHlwZXMuIApUaGVzZSBtYXJrZXJzIGFyZSBmb3VuZCBpbiBgcmVmZXJlbmNlcy9jb21iaW5lZC1tYXJrZXJzLnRzdmAgYW5kIGluY2x1ZGUgYSBzbWFsbGVyIHNldCBvZiBtYXJrZXJzIGZvciBlYWNoIG9mIHRoZSBjZWxsIHR5cGVzIHdlIGhhdmUgaWRlbnRpZmllZC4gCgpgYGB7cn0KdmFsaWRhdGlvbl9tYXJrZXJzX2RmIDwtIHJlYWRyOjpyZWFkX3Rzdih2YWxpZGF0aW9uX21hcmtlcnNfZmlsZSkKCiMgcHVsbCBvdXQgbGlzdCBvZiBnZW5lcyAKZ2VuZXMgPC0gdmFsaWRhdGlvbl9tYXJrZXJzX2RmIHw+CiAgZHBseXI6OnB1bGwoZW5zZW1ibF9nZW5lX2lkKQoKIyBnZXQgaW5kaXZpZHVhbCBnZW5lIGNvdW50cyBmb3IgYWxsIG1hcmtlciBnZW5lcyAKZ2VuZV9jdHMgPC0gbG9nY291bnRzKHNjZVtnZW5lcywgXSkgfD4KICBhcy5tYXRyaXgoKSB8PgogIHQoKSB8PiAKICBhcy5kYXRhLmZyYW1lKCkKZ2VuZV9jdHMkYmFyY29kZXMgPC0gcm93bmFtZXMoZ2VuZV9jdHMpCgojIGdldCBhbGwgdW5pcXVlIGNlbGwgdHlwZXMgZnJvbSB0aGUgZmluYWwgbHVtcGVkIGdyb3VwIApjZWxsdHlwZXNfZGYgPC0gYWxsX2luZm9fZGYgfD4gCiAgZHBseXI6OnNlbGVjdChiYXJjb2RlcywgZmluYWxfbHVtcGVkKQoKIyBjcmVhdGUgYSBkZiB0aGF0IGhhcyBnZW5lIGV4cHJlc3Npb24gY29sdW1uIGFuZCBjb2x1bW4gaW5kaWNhdGluZyB3aGV0aGVyIG9yIG5vdCB0aGUgZ2VuZSBpcyBkZXRlY3RlZCBpbiB0aGF0IGNlbGwgCmdlbmVzX2RmIDwtIGdlbmVfY3RzIHw+IAogIHRpZHlyOjpwaXZvdF9sb25nZXIoIWJhcmNvZGVzLCBuYW1lc190byA9ICJlbnNlbWJsX2dlbmVfaWQiLCB2YWx1ZXNfdG8gPSAiZ2VuZV9leHAiKSB8PiAKICBkcGx5cjo6bGVmdF9qb2luKGNlbGx0eXBlc19kZiwgYnkgPSBjKCJiYXJjb2RlcyIpKSB8PiAKICBkcGx5cjo6bGVmdF9qb2luKHZhbGlkYXRpb25fbWFya2Vyc19kZiwgYnkgPSBjKCJlbnNlbWJsX2dlbmVfaWQiKSkgfD4gCiAgZHBseXI6OnJvd3dpc2UoKSB8PiAKICBkcGx5cjo6bXV0YXRlKAogICAgZGV0ZWN0ZWQgPSBnZW5lX2V4cCA+IDAgIyBjb2x1bW4gaW5kaWNhdGluZyBpZiBnZW5lIGlzIHByZXNlbnQgb3Igbm90CiAgKQoKIyBnZXQgdG90YWwgbnVtYmVyIG9mIGNlbGxzIHBlciBmaW5hbCBhbm5vdGF0aW9uIGdyb3VwIAp0b3RhbF9jZWxsc19kZiA8LSBnZW5lc19kZiB8PiAKICBkcGx5cjo6c2VsZWN0KGJhcmNvZGVzLCBmaW5hbF9sdW1wZWQpIHw+IAogIHVuaXF1ZSgpIHw+IAogIGRwbHlyOjpjb3VudChmaW5hbF9sdW1wZWQsIG5hbWUgPSAidG90YWxfY2VsbHMiKQoKIyBnZXQgdG90YWwgbnVtYmVyIG9mIGNlbGxzIGVhY2ggZ2VuZSBpcyBkZXRlY3RlZCBpbiBwZXIgZ3JvdXAgCiMgYW5kIG1lYW4gZ2VuZSBleHByZXNzaW9uIHBlciBncm91cCAKZ3JvdXBfc3RhdHNfZGYgPC0gZ2VuZXNfZGYgfD4gCiAgZHBseXI6Omdyb3VwX2J5KGZpbmFsX2x1bXBlZCwgZW5zZW1ibF9nZW5lX2lkKSB8PgogIGRwbHlyOjpzdW1tYXJpemUoCiAgICBkZXRlY3RlZF9jb3VudCA9IHN1bShkZXRlY3RlZCksCiAgICBtZWFuX2V4cCA9IG1lYW4oZ2VuZV9leHApCiAgKQoKZ2VuZV9zdW1tYXJ5X2RmIDwtIGdlbmVzX2RmIHw+IAogICMgYWRkIHRvdGFsIGNlbGxzCiAgZHBseXI6OmxlZnRfam9pbih0b3RhbF9jZWxsc19kZiwgYnkgPSBjKCJmaW5hbF9sdW1wZWQiKSkgfD4gCiAgIyBhZGQgcGVyIGdlbmUgc3RhdHMKICBkcGx5cjo6bGVmdF9qb2luKGdyb3VwX3N0YXRzX2RmLCBieSA9IGMoImVuc2VtYmxfZ2VuZV9pZCIsICJmaW5hbF9sdW1wZWQiKSkgfD4gCiAgZHBseXI6OnNlbGVjdChnZW5lX3N5bWJvbCwgZmluYWxfbHVtcGVkLCBjZWxsX3R5cGUsIHRvdGFsX2NlbGxzLCBkZXRlY3RlZF9jb3VudCwgbWVhbl9leHApIHw+IAogIHVuaXF1ZSgpIHw+CiAgZHBseXI6OnJvd3dpc2UoKSB8PiAKICBkcGx5cjo6bXV0YXRlKAogICAgIyBnZXQgdG90YWwgcGVyY2VudAogICAgcGVyY2VudF9leHAgPSAoZGV0ZWN0ZWRfY291bnQvdG90YWxfY2VsbHMpICogMTAwLAogICAgIyBvcmRlciBnZW5lcyBiYXNlZCBvbiBjZWxsIHR5cGUgdGhleSBpbmRpY2F0ZQogICAgZ2VuZV9zeW1ib2wgPSBmYWN0b3IoZ2VuZV9zeW1ib2wsIGxldmVscyA9IHZhbGlkYXRpb25fbWFya2Vyc19kZiRnZW5lX3N5bWJvbCksCiAgICBmaW5hbF9sdW1wZWQgPSBmb3JjYXRzOjpmY3RfcmVsZXZlbChmaW5hbF9sdW1wZWQsIGNlbGxfdHlwZV9vcmRlcikKICAgIAogICkgCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9N30KIyBmaWx0ZXIgb3V0IGxvdyBleHByZXNzZWQgZ2VuZXMKZG90cGxvdF9kZiA8LSBnZW5lX3N1bW1hcnlfZGYgfD4gCiAgZHBseXI6OmZpbHRlcihtZWFuX2V4cCA+IDAsIHBlcmNlbnRfZXhwID4gMTApCgoKZG90cGxvdCA8LSBnZ3Bsb3QoZG90cGxvdF9kZiwgYWVzKHkgPSBmb3JjYXRzOjpmY3RfcmV2KGZpbmFsX2x1bXBlZCksIHggPSBnZW5lX3N5bWJvbCwgY29sb3IgPSBtZWFuX2V4cCwgc2l6ZSA9IHBlcmNlbnRfZXhwKSkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJtYWdtYSIpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMC41KQogICkgKwogIGxhYnMoCiAgICB4ID0gIiIsCiAgICB5ID0gIiIKICApCgpjb2xvcl9iYXIgPC0gZ2dwbG90KGRvdHBsb3RfZGYsIGFlcyh4ID0gZ2VuZV9zeW1ib2wsIHkgPSAxLCBmaWxsID0gY2VsbF90eXBlKSkgKyAKICBnZW9tX3RpbGUoKSArIAogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAnU2V0MScpICsKICBnZ21hcDo6dGhlbWVfbm90aGluZygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIGxhYnMoZmlsbCA9ICIiKQoKZG90cGxvdCArIGNvbG9yX2JhciArCiAgcGF0Y2h3b3JrOjpwbG90X2xheW91dChuY29sID0gMSwgaGVpZ2h0cyA9IGMoNCwgMC4xKSkgCmBgYAoKQSBmZXcgbm90ZXMgZnJvbSB0aGlzIHBsb3Q6IAoKLSBHZW5lcmFsbHkgdHVtb3IgbWFya2VycyBhcmUgcHJlc2VudCB0aHJvdWdob3V0LCBhbHRob3VnaCB0aGV5IGFyZSBoaWdoZXN0IGluIHR1bW9yIGNlbGxzLiAKLSBUdW1vciBFV1MgbG93IENBRnMgc2hvdyBwcmV0dHkgc3Ryb25nIGV4cHJlc3Npb24gb2YgdGhlIGBFV1MtbG93YCBtYXJrZXJzLiAKVGhlc2UgbWFya2VycyBhcmUgYWxzbyBwcmVzZW50IGluIGZpYnJvYmxhc3RzLCBidXQgdG8gYSBsZXNzZXIgZGVncmVlLiAKQWRkaXRpb25hbGx5LCBgVE5DYCAod2hpY2ggaGFzIGJlZW4gcHVibGlzaGVkIHRvIGJlIGltcG9ydGFudCBpbiB0aGUgbWV0YXN0YXRpYyBwaGVub3R5cGUgaW4gRXdpbmcgY2VsbHMpIGlzIHNwZWNpZmljIHRvIHRoZSBsb3cgY2VsbHMgYW5kIG5vdCBmb3VuZCBpbiB0aGUgZmlicm9ibGFzdHMsIHdoaWNoIG1ha2VzIG1lIG1vcmUgY29uZmlkZW50IHRoYXQgdGhvc2UgYXJlIGluZGVlZCB0dW1vciBjZWxscy4gCi0gRW5kb3RoZWxpYWwgY2VsbHMgc2hvdyBzdHJvbmcgZXhwcmVzc2lvbiBvZiBgUEVDQU0xYCBhbmQgYFZXRmAsIHdoaWxlIHRoYXQgaXMgbm90IHNlZW4gaW4gbW9zdCBvZiB0aGUgb3RoZXIgY2VsbHMuIAotIEFsbCBpbW11bmUgY2VsbCB0eXBlcyBzaG93IGBQVFBSQ2AgYXMgZXhwZWN0ZWQgd2l0aCBtYWNyb3BoYWdlcyBhbHNvIHNob3dpbmcgYE1SQzFgLiAKVGhlIG9ubHkgY29uY2VybiBJIHNlZSBpcyB0aGF0IHRoZSBgbWF0dXJlIFQgY2VsbGAgZG9lcyBub3QgZXhwcmVzcyBlaXRoZXIgb2YgdGhlIGBDRDNgIGdlbmVzLCBidXQgdGhlIG1lbW9yeSBUIGNlbGxzIGRvPyAKSSdtIG5vdCB0b3RhbGx5IHN1cmUgd2hhdCB0byBkbyB0aGVyZSBzaW5jZSB0aGF0IGxhYmVsIGlzIGEgY29uc2Vuc3VzIGxhYmVsIHNvIHBlcmhhcHMgdGhhdCBnZW5lIGlzIG5vdCBhcyB3ZWxsIGNvdmVyZWQgaW4gdGhlc2Ugc2FtcGxlcz8gCgoKTGV0J3MgbWFrZSBhIGhlYXRtYXAgdmVyc2lvbiBvZiB0aGUgc2FtZSBwbG90LiAKCmBgYHtyLCBmaWcud2lkdGg9N30KIyBub3RlIHRoYXQgd2UgY2FuJ3QgdXNlIHRoZSBzYW1lIGhlYXRtYXAgZnVuY3Rpb24gYXMgYmVmb3JlIHNpbmNlIHdlIGFyZSBsb29raW5nIGF0IGNlbGwgdHlwZXMgYXMgcm93cyByYXRoZXIgdGhhbiBnZW5lIHNldHMKIyB3ZSBhbHNvIHdhbnQgdG8gc3BlY2lmeSB0aGUgYW5ub3RhdGlvbiBuYW1lCgojIGZpcnN0IG1ha2UgYSBtdHggdG8gdXNlIGZvciB0aGUgaGVhdG1hcCB3aXRoIHJvd3MgYXMgY2VsbCB0eXBlcyBhbmQgZ2VuZXMgYXMgY29sdW1ucyAKaGVhdG1hcF9tdHggPC0gZ2VuZV9zdW1tYXJ5X2RmIHw+IAogIGRwbHlyOjpzZWxlY3QoZ2VuZV9zeW1ib2wsIGZpbmFsX2x1bXBlZCwgbWVhbl9leHApIHw+IAogIHRpZHlyOjpwaXZvdF93aWRlcigKICAgIG5hbWVzX2Zyb20gPSBnZW5lX3N5bWJvbCwKICAgIHZhbHVlc19mcm9tID0gbWVhbl9leHAKICApIHw+IAogIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKCJmaW5hbF9sdW1wZWQiKSB8PgogIGFzLm1hdHJpeCgpCiMgbWFrZSBzdXJlIGNlbGwgdHlwZXMgYXJlIHByZXNlbnQgaW4gdGhlIHJpZ2h0IG9yZGVyCmhlYXRtYXBfbXR4IDwtIGhlYXRtYXBfbXR4W2NlbGxfdHlwZV9vcmRlcixdCgojIGdldCBhbm5vdGF0aW9uIGNvbG9ycyBmb3IgZWFjaCBvZiB0aGUgbWFya2VyIGdlbmUgdHlwZXMgCm1hcmtlcl9nZW5lX2NhdGVnb3JpZXMgPC0gdW5pcXVlKHZhbGlkYXRpb25fbWFya2Vyc19kZiRjZWxsX3R5cGUpCm51bV9jYXRlZ29yaWVzIDwtIGxlbmd0aChtYXJrZXJfZ2VuZV9jYXRlZ29yaWVzKQpjYXRlZ29yeV9jb2xvcnMgPC0gcGFsZXR0ZS5jb2xvcnMocGFsZXR0ZSA9ICJEYXJrMiIpIHw+CiAgaGVhZChuID0gbnVtX2NhdGVnb3JpZXMpIHw+CiAgcHVycnI6OnNldF9uYW1lcyhtYXJrZXJfZ2VuZV9jYXRlZ29yaWVzKQoKCiMgY3JlYXRlIGFubm90YXRpb24gZm9yIGhlYXRtYXAKYW5ub3RhdGlvbiA8LSBDb21wbGV4SGVhdG1hcDo6Y29sdW1uQW5ub3RhdGlvbigKICBtYXJrZXJfZ2VuZV9jYXRlZ29yeSA9IHZhbGlkYXRpb25fbWFya2Vyc19kZiRjZWxsX3R5cGUsCiAgY29sID0gbGlzdCgKICAgIG1hcmtlcl9nZW5lX2NhdGVnb3J5ID0gY2F0ZWdvcnlfY29sb3JzCiAgKQopCgpDb21wbGV4SGVhdG1hcDo6SGVhdG1hcCgKICAgIGhlYXRtYXBfbXR4LAogICAgIyBzZXQgdGhlIGNvbG9yIHNjYWxlIGJhc2VkIG9uIG1pbiBhbmQgbWF4IHZhbHVlcwogICAgY29sID0gY2lyY2xpemU6OmNvbG9yUmFtcDIoc2VxKG1pbihoZWF0bWFwX210eCksIG1heChoZWF0bWFwX210eCksIGxlbmd0aCA9IDIpLCBjb2xvcnMgPSBjKCJ3aGl0ZSIsICIjMDAyNzRDIikpLAogICAgYm9yZGVyID0gVFJVRSwKICAgICMjIFJvdyBwYXJhbWV0ZXJzCiAgICBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwKICAgIHJvd190aXRsZSA9ICIiLAogICAgcm93X3RpdGxlX3NpZGUgPSAibGVmdCIsCiAgICByb3dfbmFtZXNfc2lkZSA9ICJsZWZ0IiwKICAgIHJvd19kZW5kX3NpZGUgPSAicmlnaHQiLAogICAgcm93X25hbWVzX2dwID0gZ3JpZDo6Z3Bhcihmb250c2l6ZSA9IDEwKSwKICAgICMjIENvbHVtbiBwYXJhbWV0ZXJzCiAgICBjbHVzdGVyX2NvbHVtbnMgPSBGQUxTRSwKICAgIHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgIGNvbHVtbl9uYW1lc19ncCA9IGdyaWQ6OmdwYXIoZm9udHNpemUgPSA4KSwKICAgIHRvcF9hbm5vdGF0aW9uID0gYW5ub3RhdGlvbiwKICAgIGhlYXRtYXBfbGVnZW5kX3BhcmFtID0gbGlzdCgKICAgICAgdGl0bGUgPSAiTWVhbiBleHByZXNzaW9uIgogICAgKQogICkKYGBgCgoKQW5kIGZpbmFsbHkgd2UnbGwgbG9vayBhdCBvdXIgYW5ub3RhdGlvbnMgb24gYSBVTUFQISAKQmVjYXVzZSwgd2h5IG5vdC4gCgpgYGB7cn0KZ2dwbG90KGFsbF9pbmZvX2RmLCBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gZmluYWxfbHVtcGVkKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHNpemUgPSAwLjAxKSArCiAgIyBzZXQgY29sb3IgZm9yIGFsbCByZW1haW5pbmcgY2VsbCB0eXBlcyBzaW5jZSB0aGVyZSBhcmUgbW9yZSBjb2xvcnMgdGhhbiBpbiB0aGUgcGFsZXR0ZQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKHBhbGV0dGUuY29sb3JzKHBhbGV0dGUgPSAiU2V0MSIpLCAiZ3JleTY1IiwgImdyZXk5MCIpKSArCiAgbGFicyhjb2xvciA9ICIiKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3QoYWxwaGEgPSAxLCBzaXplID0gMS41KSkpCmBgYAoKIyMgRXhwb3J0IGNlbGwgdHlwZXMgCgpOb3cgdGhhdCB3ZSBoYXZlIG91ciBjZWxsIHR5cGVzIGxldCdzIGV4cG9ydCB0aGVtIHRvIGEgVFNWIHRvIHNhdmUgZm9yIGZ1dHVyZSB1c2UhIAoKYGBge3J9CmFubm90YXRpb25fZGYgPC0gYWxsX2luZm9fZGYgfD4gCiAgZHBseXI6Om11dGF0ZSgKICAgICMgcmVtb3ZlIGxpYnJhcnkgaWQgZnJvbSBiYXJjb2RlcyBzaW5jZSB3ZSBoYXZlIGEgbGlicmFyeSBpZCBjb2x1bW4gYWxyZWFkeSAKICAgIGJhcmNvZGVzID0gc3RyaW5ncjo6d29yZChiYXJjb2RlcywgLTEsIHNlcCA9ICItIiksCiAgICAjIGFzc2lnbiBvbnRvbG9neSBJRCB1c2luZyBjb25zZW5zdXMgb250b2xvZ3kgSUQKICAgICMgYW55dGhpbmcgd2l0aG91dCBhbiBJRCBpcyBlaXRoZXIgdW5rbm93biBvciB0dW1vciAKICAgIGZpbmFsX29udG9sb2d5ID0gZHBseXI6OmlmX2Vsc2UoCiAgICAgIGZpbmFsX2Fubm90YXRpb24gPT0gY29uc2Vuc3VzX2Fubm90YXRpb24sCiAgICAgIGNvbnNlbnN1c19vbnRvbG9neSwKICAgICAgZmluYWxfYW5ub3RhdGlvbgogICAgKQogICkgfD4gCiAgZHBseXI6OnNlbGVjdCgKICAgIGJhcmNvZGVzLAogICAgbGlicmFyeV9pZCwKICAgIHNhbXBsZV9pZCwKICAgIHNhbXBsZV90eXBlLAogICAgc2luZ2xlcl9vbnRvbG9neSwKICAgIHNpbmdsZXJfYW5ub3RhdGlvbiwKICAgIGNvbnNlbnN1c19hbm5vdGF0aW9uLAogICAgY29uc2Vuc3VzX29udG9sb2d5LAogICAgZmluYWxfYW5ub3RhdGlvbiwKICAgIGZpbmFsX29udG9sb2d5CiAgKQoKcmVhZHI6OndyaXRlX3Rzdihhbm5vdGF0aW9uX2RmLCBvdXRwdXRfZmlsZSkKYGBgCgoKIyMgU2Vzc2lvbiBpbmZvIAoKYGBge3Igc2Vzc2lvbiBpbmZvfQojIHJlY29yZCB0aGUgdmVyc2lvbnMgb2YgdGhlIHBhY2thZ2VzIHVzZWQgaW4gdGhpcyBhbmFseXNpcyBhbmQgb3RoZXIgZW52aXJvbm1lbnQgaW5mb3JtYXRpb24Kc2Vzc2lvbkluZm8oKQpgYGAKCg==